package com.hrs.interceptors;

import com.alibaba.fastjson.JSONObject;
import com.hrs.common.anno.NoNeedLogin;
import com.hrs.common.anno.NoValidPrivilege;
import com.hrs.common.anno.NotOpen;
import com.hrs.common.constant.RedisConstants;
import com.hrs.common.constant.RequestUserData;
import com.hrs.common.result.CodeMsg;
import com.hrs.common.result.Result;
import com.hrs.core.service.PrivilegeService;
import com.hrs.third.RedisService;
import com.hrs.util.RequestUserUtil;
import com.hrs.util.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author 计算机系 ITAEM 摆渡人
 * @description
 * @date 2022/11/20 22:52
 */
@Slf4j
@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {

    public static final String TOKEN_NAME = "request-token";

    //路径匹配器，支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    public static final String[] urls = new String[]{
        "/v3/api-docs","/v2/api-docs","/swagger-ui.html","/error"
    };

    @Autowired
    private PrivilegeService privilegeService;

    @Autowired
    private RedisService redisService;

    /**
     * 拦截服务器端响应处理ajax请求返回结果
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //跨域设置
        this.crossDomainConfig(response);
        boolean isHandlerMethod = handler instanceof HandlerMethod;
        if (! isHandlerMethod) {
            return true;
        }

        //不开放的注解
        boolean isNotOpen = ((HandlerMethod) handler).getMethodAnnotation(NotOpen.class) != null;
        if (isNotOpen){
            this.outputResult(response, CodeMsg.NOT_OPEN);
            return false;
        }

        //不需要登录的注解
        boolean isNoNeedLogin = ((HandlerMethod) handler).getMethodAnnotation(NoNeedLogin.class) != null;
        if (isNoNeedLogin) {
            return true;
        }

        //放行的Uri前缀
        String uri = request.getRequestURI();
        log.info("requestURI:{}",uri);
        for(String url : urls){
            if(PATH_MATCHER.match(url,uri)){
                return true;
            }
        }

        //需要做token校验, 消息头的token优先于请求query参数的token
        String xHeaderToken = request.getHeader(TOKEN_NAME);
        String xRequestToken = request.getParameter(TOKEN_NAME);
        String xAccessToken = null != xHeaderToken ? xHeaderToken : xRequestToken;
        if (null == xAccessToken) {
            this.outputResult(response, CodeMsg.NOT_LOGIN);
            return false;
        }

        //根据token获取登录用户
        Long uid = TokenUtil.getUidByToken(xAccessToken);

        //token无效或token过期
        if(Objects.isNull(uid)){
            this.outputResult(response,CodeMsg.NOT_LOGIN);
            return false;
        }

        //根据uid从缓存获取用户数据
        RequestUserData requestUserData = redisService.getObject(RedisConstants.LOGIN_USER_REQUEST_DATA + uid,
                RequestUserData.class);
        if (Objects.isNull(requestUserData)){
            //redis过期也是无效登录
            this.outputResult(response,CodeMsg.NOT_LOGIN);
            return false;
        }

        //判断账号是否被停用
        if(requestUserData.getIsDisabled() == 1){
            this.outputResult(response,new CodeMsg("账号已被管理员停止使用！"));
            return false;
        }

        //判断接口权限
        Method m = ((HandlerMethod) handler).getMethod();
        Class<?> cls = ((HandlerMethod) handler).getBeanType();
        boolean isClzAnnotation = cls.isAnnotationPresent(NoValidPrivilege.class);
        boolean isMethodAnnotation = m.isAnnotationPresent(NoValidPrivilege.class);
        NoValidPrivilege noValidPrivilege = null;
        if (isClzAnnotation) {
            noValidPrivilege = cls.getAnnotation(NoValidPrivilege.class);
        } else if (isMethodAnnotation) {
            noValidPrivilege = m.getAnnotation(NoValidPrivilege.class);
        }

        //不需验证权限
        if (noValidPrivilege != null) {
            RequestUserUtil.setUser(requestUserData);
            return true;
        }

        //需要验证权限
        boolean privilegeValidPass = privilegeService.checkRoleHavePrivilege(requestUserData.getRoleId(),
                request.getRequestURI());
        if (!privilegeValidPass) {
            this.outputResult(response, CodeMsg.NOT_HAVE_PRIVILEGES);
            return false;
        }

        RequestUserUtil.setUser(requestUserData);
        return true;
    }

    /**
     * 配置跨域
     *
     * @param response
     */
    private void crossDomainConfig(HttpServletResponse response) {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
        response.setHeader("Access-Control-Expose-Headers", "*");
        response.setHeader("Access-Control-Allow-Headers", "Authentication,Origin, X-Requested-With, Content-Type, " + "Accept, x-access-token");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Expires ", "-1");
    }

    /**
     * 错误输出
     *
     * @param response
     * @param codeMsg
     * @throws IOException
     */
    private void outputResult(HttpServletResponse response, CodeMsg codeMsg) throws IOException {
        Result<Object> error = Result.error(codeMsg);
        String msg = JSONObject.toJSONString(error);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(msg);
        response.flushBuffer();
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
                                Exception ex) throws Exception {
        RequestUserUtil.removeUser();
    }
}
